package net.simflow.swf.analyzer.fileprinter;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map.Entry;
import java.util.TreeMap;

import net.simflow.swf.analyzer.exec.data.ServiceOverAbilityRecord;
import net.simflow.swf.analyzer.exec.data.TaskExecResult;
import net.simflow.swf.analyzer.exec.stats.data.ExecStats;
import net.simflow.swf.analyzer.exec.stats.data.FlowExecTime;
import net.simflow.swf.analyzer.exec.stats.data.ServiceWaitTime;
import net.simflow.swf.analyzer.network.data.Flow;
import net.simflow.swf.analyzer.network.data.Service;

public class ExecStatsFilePrinter {

	private final static LinkedHashMap<String, Boolean> DEFAULT_CONFIG = new LinkedHashMap<String, Boolean>();

	static {
		DEFAULT_CONFIG.put(ExecStats.EXECUTION_TIME_DISTRIBUTION, true);
		DEFAULT_CONFIG.put(ExecStats.FLOW_EXEC_TIME, true);
		DEFAULT_CONFIG.put(ExecStats.FLOW_AVG_EXEC_TIME_DISTRIBUTION, true);
		DEFAULT_CONFIG.put(ExecStats.FLOW_MAX_EXEC_TIME_DISTRIBUTION, true);
		DEFAULT_CONFIG.put(ExecStats.FLOW_MIN_EXEC_TIME_DISTRIBUTION, true);
		DEFAULT_CONFIG.put(ExecStats.FLOW_TOTAL_COUNT_DISTRIBUTION, true);
		DEFAULT_CONFIG.put(ExecStats.FLOW_EXECUTED_COUNT_DISTRIBUTION, true);
		DEFAULT_CONFIG.put(ExecStats.FLOW_UNFINISHED_COUNT_DISTRIBUTION, true);
		DEFAULT_CONFIG.put(ExecStats.FLOW_OVER_ABILITY_TIME_DISTRIBUTION, true);
		DEFAULT_CONFIG.put(ExecStats.FLOW_TOTAL_COUNT, true);
		DEFAULT_CONFIG.put(ExecStats.FLOW_EXECUTED_COUNT, true);
		DEFAULT_CONFIG.put(ExecStats.FLOW_UNFINISHED_COUNT, true);
		DEFAULT_CONFIG.put(ExecStats.FLOW_OVER_ABILITY_TIME, true);
		DEFAULT_CONFIG.put(ExecStats.SERVICE_TOTAL_COUNT_DISTRIBUTION, true);
		DEFAULT_CONFIG.put(ExecStats.SERVICE_EXECUTED_COUNT_DISTRIBUTION, true);
		DEFAULT_CONFIG.put(ExecStats.SERVICE_UNFINISHED_COUNT_DISTRIBUTION,
				true);
		DEFAULT_CONFIG.put(ExecStats.SERVICE_OVER_ABILITY_TIME_DISTRIBUTION,
				true);
		DEFAULT_CONFIG.put(ExecStats.SERVICE_TOTAL_COUNT, true);
		DEFAULT_CONFIG.put(ExecStats.SERVICE_EXECUTED_COUNT, true);
		DEFAULT_CONFIG.put(ExecStats.SERVICE_UNFINISHED_COUNT, true);
		DEFAULT_CONFIG.put(ExecStats.SERVICE_OVER_ABILITY_TIME, true);
		DEFAULT_CONFIG.put(ExecStats.SERVICE_WAIT_TIME, true);
		DEFAULT_CONFIG.put(ExecStats.SERVICE_AVG_WAIT_TIME_DISTRIBUTION, true);
		DEFAULT_CONFIG.put(ExecStats.SERVICE_MAX_WAIT_TIME_DISTRIBUTION, true);
		DEFAULT_CONFIG.put(ExecStats.SERVICE_MIN_WAIT_TIME_DISTRIBUTION, true);
	}

	private static ExecStatsFilePrinter instance = new ExecStatsFilePrinter();

	private ExecStatsFilePrinter() {
	}

	public static ExecStatsFilePrinter getInstance() {
		return instance;
	}

	public void print(FilePrinter p, ExecStats execStats) {
		if (p == null) {
			return;
		}
		if (execStats == null) {
			return;
		}

		p.print(FilePrinter.NEW_LINE + "Execution Stats:");

		for (Entry<String, Boolean> en : DEFAULT_CONFIG.entrySet()) {
			if (!en.getValue()) {
				continue;
			}
			String title = en.getKey();
			switch (title) {
			case ExecStats.SERVICE_TOTAL_COUNT:
				this.printServiceCountMapString(p, title,
						execStats.getServiceTotalCountMap());
				break;
			case ExecStats.SERVICE_EXECUTED_COUNT:
				this.printServiceCountMapString(p, title,
						execStats.getServiceExecCountMap());
				break;
			case ExecStats.SERVICE_UNFINISHED_COUNT:
				this.printServiceCountMapString(p, title,
						execStats.getServiceUnfinishedCountMap());
				break;
			case ExecStats.SERVICE_TOTAL_COUNT_DISTRIBUTION:
				this.printServiceDistributionString(p, title,
						execStats.getServiceTotalCountDistribution());
				break;
			case ExecStats.SERVICE_EXECUTED_COUNT_DISTRIBUTION:
				this.printServiceDistributionString(p, title,
						execStats.getServiceExecCountDistribution());
				break;
			case ExecStats.SERVICE_UNFINISHED_COUNT_DISTRIBUTION:
				this.printServiceDistributionString(p, title,
						execStats.getServiceUnfinishedCountDistribution());
				break;
			case ExecStats.EXECUTION_TIME_DISTRIBUTION:
				this.printExecTimeDistributionString(p, title, execStats);
				break;
			case ExecStats.SERVICE_OVER_ABILITY_TIME:
				this.printServiceCountMapString(p, title,
						execStats.getServiceOverAbilityTimeMap());
				break;
			case ExecStats.SERVICE_OVER_ABILITY_TIME_DISTRIBUTION:
				this.printServiceDistributionString(p, title,
						execStats.getServiceOverAbilityTimeDistribution());
				break;
			case ExecStats.FLOW_TOTAL_COUNT:
				this.printFlowCountMapString(p, title,
						execStats.getFlowTotalCountMap());
				break;
			case ExecStats.FLOW_EXECUTED_COUNT:
				this.printFlowCountMapString(p, title,
						execStats.getFlowExecCountMap());
				break;
			case ExecStats.FLOW_UNFINISHED_COUNT:
				this.printFlowCountMapString(p, title,
						execStats.getFlowUnfinishedCountMap());
				break;
			case ExecStats.FLOW_TOTAL_COUNT_DISTRIBUTION:
				this.printFlowDistributionString(p, title,
						execStats.getFlowTotalCountDistribution());
				break;
			case ExecStats.FLOW_EXECUTED_COUNT_DISTRIBUTION:
				this.printFlowDistributionString(p, title,
						execStats.getFlowExecCountDistribution());
				break;
			case ExecStats.FLOW_UNFINISHED_COUNT_DISTRIBUTION:
				this.printFlowDistributionString(p, title,
						execStats.getFlowUnfinishedCountDistribution());
				break;
			case ExecStats.FLOW_OVER_ABILITY_TIME:
				this.printFlowOverAbilityMapString(p, title,
						execStats.getFlowOverAbilityTimeMap());
				break;
			case ExecStats.FLOW_OVER_ABILITY_TIME_DISTRIBUTION:
				this.printFlowDistributionString(p, title,
						execStats.getFlowOverAbilityTimeDistribution());
				break;
			case ExecStats.SERVICE_WAIT_TIME:
				this.printServiceWaitTimeMapString(p, title,
						execStats.getServiceWaitTimeMap());
				break;
			case ExecStats.SERVICE_AVG_WAIT_TIME_DISTRIBUTION:
				this.printServiceDistributionString(p, title,
						execStats.getServiceAvgWaitTimeDistribution());
				break;
			case ExecStats.SERVICE_MAX_WAIT_TIME_DISTRIBUTION:
				this.printServiceDistributionString(p, title,
						execStats.getServiceMaxWaitTimeDistribution());
				break;
			case ExecStats.SERVICE_MIN_WAIT_TIME_DISTRIBUTION:
				this.printServiceDistributionString(p, title,
						execStats.getServiceMinWaitTimeDistribution());
				break;
			case ExecStats.FLOW_EXEC_TIME:
				this.printFlowExecTimeMapString(p, title,
						execStats.getFlowExecTimeMap());
				break;
			case ExecStats.FLOW_AVG_EXEC_TIME_DISTRIBUTION:
				this.printFlowDistributionString(p, title,
						execStats.getFlowAvgExecTimeDistribution());
				break;
			case ExecStats.FLOW_MAX_EXEC_TIME_DISTRIBUTION:
				this.printFlowDistributionString(p, title,
						execStats.getFlowMaxExecTimeDistribution());
				break;
			case ExecStats.FLOW_MIN_EXEC_TIME_DISTRIBUTION:
				this.printFlowDistributionString(p, title,
						execStats.getFlowMinExecTimeDistribution());
				break;
			}
		}
	}

	public void print(FilePrinter p, ExecStats execStats, Flow flow) {
		if (p == null) {
			return;
		}
		if (execStats == null) {
			return;
		}

		p.print(FilePrinter.NEW_LINE + "Execution Stats:");
		for (Entry<String, Boolean> en : DEFAULT_CONFIG.entrySet()) {
			if (!en.getValue()) {
				continue;
			}
			String title = en.getKey();
			switch (title) {
			case ExecStats.SERVICE_TOTAL_COUNT:
				this.printServiceCountMapString(p, title,
						execStats.getServiceTotalCountMap(), flow);
				break;
			case ExecStats.SERVICE_EXECUTED_COUNT:
				this.printServiceCountMapString(p, title,
						execStats.getServiceExecCountMap(), flow);
				break;
			case ExecStats.SERVICE_UNFINISHED_COUNT:
				this.printServiceCountMapString(p, title,
						execStats.getServiceUnfinishedCountMap(), flow);
				break;
			case ExecStats.SERVICE_TOTAL_COUNT_DISTRIBUTION:
				this.printServiceDistributionString(p, title,
						execStats.getServiceTotalCountDistribution(), flow);
				break;
			case ExecStats.SERVICE_EXECUTED_COUNT_DISTRIBUTION:
				this.printServiceDistributionString(p, title,
						execStats.getServiceExecCountDistribution(), flow);
				break;
			case ExecStats.SERVICE_UNFINISHED_COUNT_DISTRIBUTION:
				this.printServiceDistributionString(p, title,
						execStats.getServiceUnfinishedCountDistribution(), flow);
				break;
			case ExecStats.EXECUTION_TIME_DISTRIBUTION:
				this.printExecTimeDistributionString(p, title, execStats, flow);
				break;
			case ExecStats.SERVICE_OVER_ABILITY_TIME:
				this.printServiceCountMapString(p, title,
						execStats.getServiceOverAbilityTimeMap(), flow);
				break;
			case ExecStats.SERVICE_OVER_ABILITY_TIME_DISTRIBUTION:
				this.printServiceDistributionString(p, title,
						execStats.getServiceOverAbilityTimeDistribution(), flow);
				break;
			case ExecStats.FLOW_TOTAL_COUNT:
				this.printFlowCountMapString(p, title,
						execStats.getFlowTotalCountMap(), flow);
				break;
			case ExecStats.FLOW_EXECUTED_COUNT:
				this.printFlowCountMapString(p, title,
						execStats.getFlowExecCountMap(), flow);
				break;
			case ExecStats.FLOW_UNFINISHED_COUNT:
				this.printFlowCountMapString(p, title,
						execStats.getFlowUnfinishedCountMap(), flow);
				break;
			case ExecStats.FLOW_TOTAL_COUNT_DISTRIBUTION:
				this.printFlowDistributionString(p, title,
						execStats.getFlowTotalCountDistribution(), flow);
				break;
			case ExecStats.FLOW_EXECUTED_COUNT_DISTRIBUTION:
				this.printFlowDistributionString(p, title,
						execStats.getFlowExecCountDistribution(), flow);
				break;
			case ExecStats.FLOW_UNFINISHED_COUNT_DISTRIBUTION:
				this.printFlowDistributionString(p, title,
						execStats.getFlowUnfinishedCountDistribution(), flow);
				break;
			case ExecStats.FLOW_OVER_ABILITY_TIME:
				this.printFlowOverAbilityMapString(p, title,
						execStats.getFlowOverAbilityTimeMap(), flow);
				break;
			case ExecStats.FLOW_OVER_ABILITY_TIME_DISTRIBUTION:
				this.printFlowDistributionString(p, title,
						execStats.getFlowOverAbilityTimeDistribution(), flow);
				break;
			case ExecStats.SERVICE_WAIT_TIME:
				this.printServiceWaitTimeMapString(p, title,
						execStats.getServiceWaitTimeMap(), flow);
				break;
			case ExecStats.SERVICE_AVG_WAIT_TIME_DISTRIBUTION:
				this.printServiceDistributionString(p, title,
						execStats.getServiceAvgWaitTimeDistribution(), flow);
				break;
			case ExecStats.SERVICE_MAX_WAIT_TIME_DISTRIBUTION:
				this.printServiceDistributionString(p, title,
						execStats.getServiceMaxWaitTimeDistribution(), flow);
				break;
			case ExecStats.SERVICE_MIN_WAIT_TIME_DISTRIBUTION:
				this.printServiceDistributionString(p, title,
						execStats.getServiceMinWaitTimeDistribution(), flow);
				break;
			case ExecStats.FLOW_EXEC_TIME:
				this.printFlowExecTimeMapString(p, title,
						execStats.getFlowExecTimeMap(), flow);
				break;
			case ExecStats.FLOW_AVG_EXEC_TIME_DISTRIBUTION:
				this.printFlowDistributionString(p, title,
						execStats.getFlowAvgExecTimeDistribution(), flow);
				break;
			case ExecStats.FLOW_MAX_EXEC_TIME_DISTRIBUTION:
				this.printFlowDistributionString(p, title,
						execStats.getFlowMaxExecTimeDistribution(), flow);
				break;
			case ExecStats.FLOW_MIN_EXEC_TIME_DISTRIBUTION:
				this.printFlowDistributionString(p, title,
						execStats.getFlowMinExecTimeDistribution(), flow);
				break;
			}
		}
	}

	public void printServiceCountMapString(FilePrinter p, String title,
			HashMap<Service, Integer> serviceCountMap) {
		if (serviceCountMap == null) {
			return;
		}

		boolean is = true;
		for (Entry<Service, Integer> en : serviceCountMap.entrySet()) {
			if (is) {
				p.print(FilePrinter.NEW_LINE + title + ":");
				is = false;
			}
			p.print(FilePrinter.TAB + en.getKey().getNameInNetwork() + ": "
					+ en.getValue());
		}
	}

	public void printServiceCountMapString(FilePrinter p, String title,
			HashMap<Service, Integer> serviceCountMap, Flow flow) {
		if (serviceCountMap == null) {
			return;
		}

		boolean is = true;
		for (Service svc : flow.getServiceSet()) {
			if (serviceCountMap.containsKey(svc)) {
				if (is) {
					p.print(FilePrinter.NEW_LINE + title + ":");
					is = false;
				}
				p.print(FilePrinter.TAB + svc.getNameInNetwork() + ": "
						+ serviceCountMap.get(svc));
			}
		}
	}

	public void printServiceDistributionString(FilePrinter p, String title,
			TreeMap<? extends Number, ArrayList<Service>> serviceDistribution) {
		if (serviceDistribution == null) {
			return;
		}

		boolean is = true;
		for (Entry<? extends Number, ArrayList<Service>> en : serviceDistribution
				.entrySet()) {
			if (is) {
				p.print(FilePrinter.NEW_LINE + title + ":");
				is = false;
			}
			p.print(FilePrinter.TAB + en.getKey() + " (" + en.getValue().size()
					+ ")" + ":");
			for (Service svc : en.getValue()) {
				p.print(" " + svc.getNameInNetwork() + ";");
			}
		}
	}

	public void printServiceDistributionString(FilePrinter p, String title,
			TreeMap<? extends Number, ArrayList<Service>> serviceDistribution,
			Flow flow) {
		if (serviceDistribution == null) {
			return;
		}

		boolean is = true;
		for (Entry<? extends Number, ArrayList<Service>> en : serviceDistribution
				.entrySet()) {
			ArrayList<Service> temp = new ArrayList<Service>(
					flow.getServiceSet());
			temp.retainAll(en.getValue());

			if (!temp.isEmpty()) {
				if (is) {
					p.print(FilePrinter.NEW_LINE + title + ":");
					is = false;
				}
				p.print(FilePrinter.TAB + en.getKey() + " (" + temp.size()
						+ ")" + ":");
				for (Service r : temp) {
					p.print(" " + r.getNameInNetwork() + ";");
				}
			}
		}
	}

	public void printExecTimeDistributionString(FilePrinter p, String title,
			ExecStats estats) {
		if (estats.getExecTimeDistribution() == null) {
			return;
		}

		boolean is = true;
		for (Entry<Integer, ArrayList<TaskExecResult>> en : estats
				.getExecTimeDistribution().entrySet()) {
			if (is) {
				p.print(FilePrinter.NEW_LINE + title + ":");
				is = false;
			}
			p.print(FilePrinter.TAB + en.getKey() + " (" + en.getValue().size()
					+ ")" + ":");
			for (TaskExecResult r : en.getValue()) {
				p.print(" (" + r.getInfo() + ")" + ";");
			}
		}
	}

	public void printExecTimeDistributionString(FilePrinter p, String title,
			ExecStats estats, Flow flow) {
		if (estats.getExecTimeDistribution() == null) {
			return;
		}

		boolean is = true;
		for (Entry<Integer, ArrayList<TaskExecResult>> en : estats
				.getExecTimeDistribution().entrySet()) {
			ArrayList<TaskExecResult> temp = new ArrayList<TaskExecResult>();
			for (TaskExecResult r : en.getValue()) {
				if (r.getFlow() == flow) {
					temp.add(r);
				}
			}
			if (!temp.isEmpty()) {
				if (is) {
					p.print(FilePrinter.NEW_LINE + title + ":");
					is = false;
				}
				p.print(FilePrinter.TAB + en.getKey() + " (" + temp.size()
						+ ")" + ":");
				for (TaskExecResult r : temp) {
					p.print(" (" + r.getInfo() + ")" + ";");
				}
			}
		}
	}

	public void printServiceWaitTimeMapString(FilePrinter p, String title,
			HashMap<Service, ServiceWaitTime> serviceCountMap) {
		if (serviceCountMap == null) {
			return;
		}

		boolean is = true;
		for (Entry<Service, ServiceWaitTime> en : serviceCountMap.entrySet()) {
			if (is) {
				p.print(FilePrinter.NEW_LINE + title + ":");
				is = false;
			}
			p.print(FilePrinter.TAB + en.getKey().getNameInNetwork() + ": "
					+ en.getValue().toString());
		}
	}

	public void printServiceWaitTimeMapString(FilePrinter p, String title,
			HashMap<Service, ServiceWaitTime> serviceCountMap, Flow flow) {
		if (serviceCountMap == null) {
			return;
		}

		boolean is = true;
		for (Service svc : flow.getServiceSet()) {
			if (is) {
				p.print(FilePrinter.NEW_LINE + title + ":");
				is = false;
			}
			p.print(FilePrinter.TAB + svc.getNameInNetwork() + ": "
					+ serviceCountMap.get(svc).toString());
		}
	}

	public void printFlowCountMapString(FilePrinter p, String title,
			HashMap<Flow, ArrayList<TaskExecResult>> flowCountMap) {
		if (flowCountMap == null) {
			return;
		}

		boolean is = true;
		for (Entry<Flow, ArrayList<TaskExecResult>> en : flowCountMap
				.entrySet()) {
			if (is) {
				p.print(FilePrinter.NEW_LINE + title + ":");
				is = false;
			}
			p.print(FilePrinter.TAB + en.getKey().getNameInNetwork() + ": "
					+ en.getValue().size());
		}
	}

	public void printFlowCountMapString(FilePrinter p, String title,
			HashMap<Flow, ArrayList<TaskExecResult>> flowCountMap, Flow flow) {
		if (flowCountMap == null) {
			return;
		}

		ArrayList<TaskExecResult> a = flowCountMap.get(flow);
		int c;
		if (a == null) {
			c = 0;
		} else {
			c = a.size();
		}

		boolean is = true;
		if (is) {
			p.print(FilePrinter.NEW_LINE + title + ":");
			is = false;
		}
		p.print(FilePrinter.TAB + flow.getNameInNetwork() + ": " + c);
	}

	public void printFlowDistributionString(FilePrinter p, String title,
			TreeMap<? extends Number, ArrayList<Flow>> flowDistribution) {
		if (flowDistribution == null) {
			return;
		}

		boolean is = true;
		for (Entry<? extends Number, ArrayList<Flow>> en : flowDistribution
				.entrySet()) {
			if (is) {
				p.print(FilePrinter.NEW_LINE + title + ":");
				is = false;
			}
			p.print(FilePrinter.TAB + en.getKey() + " (" + en.getValue().size()
					+ ")" + ":");
			for (Flow flow : en.getValue()) {
				p.print(" " + flow.getNameInNetwork() + ";");
			}
		}
	}

	public void printFlowDistributionString(FilePrinter p, String title,
			TreeMap<? extends Number, ArrayList<Flow>> flowDistribution,
			Flow flow) {
		if (flowDistribution == null) {
			return;
		}

		boolean is = true;
		for (Entry<? extends Number, ArrayList<Flow>> en : flowDistribution
				.entrySet()) {
			if (en.getValue() != null && en.getValue().contains(flow)) {
				if (is) {
					p.print(FilePrinter.NEW_LINE + title + ":");
					is = false;
				}
				p.print(FilePrinter.TAB + en.getKey() + ":" + " "
						+ flow.getNameInNetwork() + ";");
			}
		}
	}

	public void printFlowOverAbilityMapString(FilePrinter p, String title,
			HashMap<Flow, ServiceOverAbilityRecord> flowOverAbilityMap) {
		if (flowOverAbilityMap == null) {
			return;
		}

		boolean is = true;
		for (Entry<Flow, ServiceOverAbilityRecord> en : flowOverAbilityMap
				.entrySet()) {
			if (is) {
				p.print(FilePrinter.NEW_LINE + title + ":");
				is = false;
			}
			p.print(FilePrinter.TAB + en.getKey().getNameInNetwork() + ":"
					+ " " + en.getValue().toString());
		}
	}

	public void printFlowOverAbilityMapString(FilePrinter p, String title,
			HashMap<Flow, ServiceOverAbilityRecord> flowOverAbilityMap,
			Flow flow) {
		if (flowOverAbilityMap == null) {
			return;
		}

		boolean is = true;
		ServiceOverAbilityRecord rec = flowOverAbilityMap.get(flow);
		if (rec != null) {
			if (is) {
				p.print(FilePrinter.NEW_LINE + title + ":");
				is = false;
			}
			p.print(FilePrinter.TAB + flow.getNameInNetwork() + ": "
					+ rec.toString());
		}
	}

	public void printFlowExecTimeMapString(FilePrinter p, String title,
			HashMap<Flow, FlowExecTime> flowExecTimeMap) {
		if (flowExecTimeMap == null) {
			return;
		}

		boolean is = true;
		for (Entry<Flow, FlowExecTime> en : flowExecTimeMap.entrySet()) {
			if (is) {
				p.print(FilePrinter.NEW_LINE + title + ":");
				is = false;
			}
			p.print(FilePrinter.TAB + en.getKey().getNameInNetwork() + ": "
					+ en.getValue().toString());
		}
	}

	public void printFlowExecTimeMapString(FilePrinter p, String title,
			HashMap<Flow, FlowExecTime> flowExecTimeMap, Flow flow) {
		if (flowExecTimeMap == null) {
			return;
		}

		boolean is = true;

		FlowExecTime res = flowExecTimeMap.get(flow);
		if (res != null) {
			if (is) {
				p.print(FilePrinter.NEW_LINE + title + ":");
				is = false;
			}
			p.print(FilePrinter.TAB + flow.getNameInNetwork() + ": "
					+ res.toString());
		}
	}
}
